// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#include "coin2/exchange/bitstamp/api_order/fix/fix_message_builder.h"

#include <utility>

#include <glog/logging.h>

#include "coin2/exchange/base/order/order.h"
#include "coin2/exchange/base/symbology/product.h"
#include "coin2/exchange/base/symbology/product_info.h"
#include "coin2/exchange/bitstamp/api_order/fix/fix_fields.h"
#include "coin2/exchange/bitstamp/symbology/product.h"
#include "quickfix/fix44/NewOrderSingle.h"
#include "quickfix/fix44/OrderCancelRequest.h"
#include "quickfix/fix44/OrderStatusRequest.h"

namespace coin2::exchange::bitstamp::api_order::fix {

BitstampOrder::BitstampOrder(
    const OrderExecution& ord_exec,
    GetProductHolderFunc get_product_holder) {
  Parse(ord_exec, get_product_holder);
  Validate();
}

void BitstampOrder::Parse(const OrderExecution& ord_exec, GetProductHolderFunc get_product_holder) {
  using OrderType = coin2::exchange::base::order::OrderType;

  using BitstampProduct = coin2::exchange::bitstamp::symbology::BitstampProduct;
  auto product = BitstampProduct::FromStr(
      ord_exec.order_spec->product,
      ord_exec.order_spec->order_created_time);
  auto product_holder = get_product_holder(product, ord_exec.order_spec->order_created_time);
  auto formatter = product_holder->product_info().order_formatter();

  client_id = std::to_string(ord_exec.proc_order_id);
  symbol = ord_exec.order_spec->product;
  quantity = stod(formatter.FormatQty(ord_exec.order_spec->qty));
  price = stod(formatter.FormatPrice(ord_exec.order_spec->price));
  type = ord_exec.order_spec->order_type == OrderType::MARKET_ORDER ? MARKET : LIMIT;
  side = ord_exec.order_spec->order_side == 1 ? BUY : SELL;
  // TODO(daniel) - DAY/GTC/IOC/FOK
  time_in_force = GOOD_TILL_CANCEL;
  if (ord_exec.order_spec->duration == coin::proto::OrderDuration::IOC_ORDER) {
    time_in_force = IMMEDIATE_OR_CANCEL;
  }
}

void BitstampOrder::Validate() const {
  // TODO(daniel) - validate message fields
}

auto FixMessageBuilder::GeneratePlaceOrder(
    const OrderExecution& ord_exec,
    GetProductHolderFunc get_product_holder) -> RequestTypePtr {
  BitstampOrder order{ord_exec, get_product_holder};
  RequestTypePtr message = std::make_unique<FIX44::NewOrderSingle>();
  message->setField(FIX::ClOrdID(order.client_id));
  message->setField(FIX::Symbol(order.symbol));
  message->setField(FIX::OrdType(order.type));
  message->setField(FIX::OrderQty(order.quantity));
  message->setField(FIX::Price(order.price));
  message->setField(FIX::Side(order.side));
  message->setField(FIX::TimeInForce(order.time_in_force));

  return message;
}

auto FixMessageBuilder::GenerateCancelOrder(const OrderExecution& ord_exec) -> RequestTypePtr {
  const std::string order_id_str = ord_exec.oc == nullptr ? "" : ord_exec.oc->exchange_order_id();
  const std::string client_id_str = std::to_string(ord_exec.proc_order_id);
  RequestTypePtr message = std::make_unique<FIX44::OrderCancelRequest>();
  if (!client_id_str.empty()) {
    message->setField(FIX::OrigClOrdID(client_id_str));
    message->setField(FIX::ClOrdID(client_id_str));
  }
  if (!order_id_str.empty()) {
    message->setField(FIX::OrderID(order_id_str));
  }

  return message;
}

auto FixMessageBuilder::GenerateQueryOrder(const OrderExecution& ord_exec) -> RequestTypePtr {
  LOG(WARNING) << "bitstamp does not support order query";
  return nullptr;
}

}  // namespace coin2::exchange::bitstamp::api_order::fix
